package de.gameduell.framework.player {
	import de.gameduell.framework.util.HashMap;
	import flash.display.Sprite;
	import flash.events.EventDispatcher;

	/**
	 * @author daal
	 */
	public class Player extends EventDispatcher {
		public static const PLAYING_FORWARD : String  = "playingForward";		public static const PLAYING_BACKWARD : String  = "playingBackward";
		public static const PLAYING_STEP_FORWARD : String = "playingStepForward";		public static const PLAYING_STEP_BACKWARD : String = "playingStepBackward";
		public static const STOPPED : String  = "stopped";
		public static const PAUSED : String = "paused";		public static const PAUSE_REQUIRED : String = "pauseRequired";
				private var _rounds:HashMap;
		private var _roundsCount:uint;
		private var _screen:Sprite;
		private var _scenes:Array;
		private var _currentScene:IScene;
		private var _currentRound:uint;
		private var _status : String;
		
		public function Player() {
			_roundsCount = 0;
			_currentRound = 0;
			_rounds = new HashMap();
			_screen = new Sprite();
			_scenes = new Array();
			_status = Player.STOPPED;
		}

		public function addRound(scenes:Array):void {
			_rounds.put(_roundsCount, scenes);
			_roundsCount++;
			_scenes = _rounds.getValue(_currentRound) as Array;
		}
		
		public function nextRound():void {
			if(isLastScene(_currentScene) == true && _currentRound < _roundsCount - 1) {
				goToNextRound();
			} else {
				goTo(_scenes.length);
			}
		}
		
		public function previousRound():void {
			if(_currentScene == null && _currentRound > 0) {
				_currentRound--;
				_scenes = _rounds.getValue(_currentRound) as Array;
				_currentScene = null;
				dispatchEvent(new PlayerEvent(PlayerEvent.ROUND_CHANGED));
				var progressEvent:PlayerEvent = new PlayerEvent(PlayerEvent.PROGRESS);
				progressEvent.indexOfCurrentScene = 0;
				dispatchEvent(progressEvent);
				_status = Player.PAUSED;
			} else {
				stop();
			}
		}
		
		public function playForward() : void {
			if(isLastScene(_currentScene) == true) {
				if(_currentRound == _roundsCount - 1) {
					return;
				} else {
					goToNextRound();
				}
			}
			if(_status == Player.STOPPED || _status == Player.PAUSED) {
				_status = Player.PLAYING_FORWARD;
				dispatchEvent(new PlayerEvent(PlayerEvent.PLAY));
				playNext();
			}
		}
		
		public function playStepForward() : void {
			if(isLastScene(_currentScene) == true) {
				if(_currentRound == _roundsCount - 1) {
					return;
				} else {
					goToNextRound();
				}
			}
			if(_status == Player.STOPPED || _status == Player.PAUSED) {
				_status = Player.PLAYING_STEP_FORWARD;
				var scene:IScene = nextScene();
				initScene(scene);
				_currentScene.init();
				_currentScene.play();
			}
		}

		public function playBackward() : void {
			if(_currentScene == null) {
				if(_currentRound > 0) {
					goToPreviousRound();
					goTo(_scenes.length);
				} else {
					return;
				}
			}
			if(_status == Player.STOPPED || _status == Player.PAUSED) {
				_status = Player.PLAYING_BACKWARD;
				dispatchEvent(new PlayerEvent(PlayerEvent.PLAY));
				playBack();
			}
		}

		public function playStepBackward() : void {
			if(_currentScene == null) {
				if(_currentRound > 0) {
					goToPreviousRound();
					goTo(_scenes.length);
				}
				return;
			}
			if(_status == Player.STOPPED || _status == Player.PAUSED) {
				_status = Player.PLAYING_STEP_BACKWARD;
				initScene(_currentScene);
				_currentScene.playBack();				
			}
		}
		
		private function goToNextRound() : void {
			_currentRound++;
			_scenes = _rounds.getValue(_currentRound) as Array;
			_currentScene = null;
			dispatchEvent(new PlayerEvent(PlayerEvent.ROUND_CHANGED));
			var progressEvent:PlayerEvent = new PlayerEvent(PlayerEvent.PROGRESS);
			progressEvent.indexOfCurrentScene = 0;
			dispatchEvent(progressEvent);
			_status = Player.PAUSED;
		}
		
		private function goToPreviousRound() : void {
			_currentRound--;
			_scenes = _rounds.getValue(_currentRound) as Array;
			_currentScene = null;
			dispatchEvent(new PlayerEvent(PlayerEvent.ROUND_CHANGED));
		}
		
		private function playNext() : void {
			var scene:IScene = nextScene();
			initScene(scene);
			_currentScene.init();
			_currentScene.play();
		}

		private function playBack() : void {
			initScene(_currentScene);
			_currentScene.playBack();
		}
		
		public function stop():void {
			if(_status == Player.PAUSED && _currentScene != null) {
				_status = Player.STOPPED;
				_currentScene.stop();
				var progressEvent:PlayerEvent = new PlayerEvent(PlayerEvent.PROGRESS);
				progressEvent.indexOfCurrentScene = 0; 
				dispatchEvent(progressEvent);
				_currentScene.removeEventListener(PlayerEvent.SCENE_COMPLETED, forwardSceneCompletedHandler);
				_currentScene.removeEventListener(PlayerEvent.SCENE_COMPLETED, backwardSceneCompletedHandler);
				_currentScene.removeEventListener(PlayerEvent.SCENE_COMPLETED, stepForwardCompletedHandler);
				_currentScene.removeEventListener(PlayerEvent.SCENE_COMPLETED, stepBackwardCompletedHandler);
				_currentScene = null;
				dispatchEvent(new PlayerEvent(PlayerEvent.STOP));
			}		}
		
		public function pause() : void {
			if(_status == Player.PLAYING_FORWARD || _status == Player.PLAYING_BACKWARD) {
				_currentScene.pause();
				_status = Player.PAUSE_REQUIRED;
				dispatchEvent(new PlayerEvent(PlayerEvent.PAUSE));
			}
		}
		
		public function goTo(sceneNumber:uint):void {
			if (_status != Player.PAUSED && _status != Player.STOPPED) {
				return;
			}
			_status = Player.PAUSED;
			dispatchEvent(new PlayerEvent(PlayerEvent.JUMP));
			if(_currentScene == null) {
				for(var k:int = 0; k < sceneNumber; k++) {
					_currentScene = _scenes[k];
					_scenes[k].init();
					_scenes[k].skipForward();
				}
			} else if (_scenes.indexOf(_currentScene) < sceneNumber) {
				for(var i:int = _scenes.indexOf(_currentScene) + 1; i < sceneNumber; i++) {
					_currentScene = _scenes[i];
					_scenes[i].init();
					_scenes[i].skipForward();
				}
			} else {
				for(var j:int = _scenes.indexOf(_currentScene); j > sceneNumber; j--) {
					_scenes[j].init();
					_scenes[j].skipBackward();
					_currentScene = _scenes[j - 1];
				}
			}
			var progressEvent:PlayerEvent = new PlayerEvent(PlayerEvent.PROGRESS);
			progressEvent.indexOfCurrentScene = sceneNumber;
			dispatchEvent(progressEvent);
			dispatchEvent(new PlayerEvent(PlayerEvent.START));
		}

		private function nextScene():IScene {
			if(_currentScene == null) {
				return _scenes[0];
			} else {
				return _scenes[_scenes.indexOf(_currentScene) + 1];
			}
		}
		
		private function previousScene() : IScene {
			return _scenes[_scenes.indexOf(_currentScene) - 1];
		}

		private function initScene(scene:IScene):void {
			//TODO:: doublecheck
			if(scene == null) {
				//TODO:: some finished event could be thrown.
				return;
			}
			_currentScene = scene;
			if(_status == Player.PLAYING_FORWARD){
				_currentScene.addEventListener(PlayerEvent.SCENE_COMPLETED, forwardSceneCompletedHandler);
			}			if(_status == Player.PLAYING_BACKWARD){
				_currentScene.addEventListener(PlayerEvent.SCENE_COMPLETED, backwardSceneCompletedHandler);
			}			if(_status == Player.PLAYING_STEP_FORWARD){
				_currentScene.addEventListener(PlayerEvent.SCENE_COMPLETED, stepForwardCompletedHandler);
			}			if(_status == Player.PLAYING_STEP_BACKWARD){
				_currentScene.addEventListener(PlayerEvent.SCENE_COMPLETED, stepBackwardCompletedHandler);
			}
		}
		
		private function backwardSceneCompletedHandler(event:PlayerEvent):void {
			if(_status == Player.PAUSE_REQUIRED) {
				_status = Player.PAUSED;
			}
			_currentScene.removeEventListener(PlayerEvent.SCENE_COMPLETED, backwardSceneCompletedHandler);
			var progressEvent:PlayerEvent = new PlayerEvent(PlayerEvent.PROGRESS);
			progressEvent.indexOfCurrentScene = _scenes.indexOf(_currentScene);
			dispatchEvent(progressEvent);
			if(isFirstScene(_currentScene) == false) {
				_currentScene = previousScene();
				if(_status != Player.PAUSED) {
					playBack();
				}
			}
			else {
				_currentScene = null;
				dispatchEvent(new PlayerEvent(PlayerEvent.PAUSE));
				_status = Player.STOPPED;
			}
		}
		
		private function stepForwardCompletedHandler(event:PlayerEvent):void {
			_currentScene.removeEventListener(PlayerEvent.SCENE_COMPLETED, stepForwardCompletedHandler);
			_status = Player.PAUSED;
			var progressEvent:PlayerEvent = new PlayerEvent(PlayerEvent.PROGRESS);
			progressEvent.indexOfCurrentScene = _scenes.indexOf(_currentScene) + 1;
			dispatchEvent(progressEvent); 
		}
		
		private function stepBackwardCompletedHandler(event:PlayerEvent):void {
			_currentScene.removeEventListener(PlayerEvent.SCENE_COMPLETED, stepBackwardCompletedHandler);
			_status = Player.PAUSED;
			var progressEvent:PlayerEvent = new PlayerEvent(PlayerEvent.PROGRESS);
			progressEvent.indexOfCurrentScene = _scenes.indexOf(_currentScene);
			dispatchEvent(progressEvent);
			if(isFirstScene(_currentScene) == false) {
				_currentScene = previousScene();
			}
			else {
				_currentScene = null;
			}
		}
		
		private function forwardSceneCompletedHandler(event:PlayerEvent):void {
			if(_status == Player.PAUSE_REQUIRED) {
				_status = Player.PAUSED;
			}
			_currentScene.removeEventListener(PlayerEvent.SCENE_COMPLETED, forwardSceneCompletedHandler);
			var progressEvent:PlayerEvent = new PlayerEvent(PlayerEvent.PROGRESS);
			progressEvent.indexOfCurrentScene = _scenes.indexOf(_currentScene) + 1;
			dispatchEvent(progressEvent);
			if(_status != Player.PAUSED) {
				if(isLastScene(_currentScene)== false) {
					playNext();
				}
				else {
					dispatchEvent(new PlayerEvent(PlayerEvent.PAUSE));
					_status = Player.PAUSED;
				}
			}		}
		
		private function isFirstScene(scene : IScene) : Boolean {
			return _scenes.indexOf(scene) == 0 ? true : false;
		}
		private function isLastScene(scene : IScene) : Boolean {
			return _scenes.indexOf(scene) == _scenes.length - 1 ? true : false;
		}

		public function get screen():Sprite {
			return _screen;
		}
		
		public function get scenes() : Array {
			return _scenes;
		}
		
		public function get currentRound() : uint {
			return _currentRound;
		}
	}
}
